package file

import (
	"crypto/md5"
	"encoding/hex"
	"golang.org/x/sys/windows"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

const ()

// Delete 文件夹/文件通用删除方法
func Delete(path string) error {
	err := os.RemoveAll(path)
	if err != nil {
		if strings.Index(err.Error(), "Access is denied") != -1 {
			err = GetPerm(path)
			if err != nil {
				return err
			}
			return os.RemoveAll(path)
		}

	}
	return nil
}

// GetPerm 获取win管理员(admin)权限
func GetPerm(path string) error {
	cmd := exec.Command("takeown", "/f", path, "/r", "/d", "y")
	_, err := cmd.CombinedOutput()
	time.Sleep(300)
	if err != nil {

		return err
	}
	// icacls命令，赋予Administrators完全控制权限
	cmd2 := exec.Command("icacls", path, "/grant", "Administrators:F", "/T")

	// 执行命令
	_, err = cmd2.CombinedOutput()
	time.Sleep(300)
	if err != nil {
		return err
	}
	return nil
}

type File struct {
	//文件名(包含后缀)
	Name string
	//文件大小
	Size int64
	//创建时间
	CreateTime time.Time

	//文件后缀
	Ext string
	//完整路径
	Path string
	//IsOnlyRead bool

	//修改时间
	ModifyTime time.Time
	//文件是否被隐藏(window)
	IsHidden   bool
	IsCanRead  bool
	IsCanWrite bool
}

// SetHiddenAtt 设置隐藏属性
func (f *File) SetHiddenAtt(ishidden bool) error {
	return setFileHiddenAtt(f.Path, ishidden)
}

// ToHash 获取哈希值
func (f *File) ToHash() (string, error) {
	ff, err := os.OpenFile(f.Path, os.O_RDONLY, os.ModePerm)
	if err != nil {
		return "", err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(ff)

	hash := md5.New()
	if _, err := io.Copy(hash, ff); err != nil {
		return "", err
	}

	return hex.EncodeToString(hash.Sum(nil)), nil
}

// Content 获取内容,返回[]byte指针则修改源文件内容
func (f *File) Content(fn func(bs []byte) *[]byte) error {
	ff, err := os.OpenFile(f.Path, os.O_RDONLY|os.O_WRONLY, os.ModePerm)
	if err != nil {
		return err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(ff)

	buffer, err := os.ReadFile(f.Path)
	nbs := fn(buffer)
	if nbs != nil {
		// 清空文件内容
		_ = ff.Truncate(0)
		_, _ = ff.Seek(0, 0)
		// 写入新内容
		_, err := ff.Write(*nbs)
		return err
	}
	return nil
}

// Delete 删除文件
func (f *File) Delete() error {
	return Delete(f.Path)
}

// isFileHidden 判断文件是否被隐藏(windnow)
func isFileHidden(path string) (bool, error) {
	pathPtr, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return false, err
	}
	attributes, err := windows.GetFileAttributes(pathPtr)
	if err != nil {
		return false, err
	}
	return attributes&windows.FILE_ATTRIBUTE_HIDDEN == windows.FILE_ATTRIBUTE_HIDDEN, nil

}

// setFileHiddenAtt 设置文件隐藏属性
func setFileHiddenAtt(path string, ishidden bool) error {
	pathp, err := syscall.UTF16PtrFromString(path)
	if err != nil {
		return err
	}

	if ishidden {
		// 应用新的文件属性
		return windows.SetFileAttributes(pathp, windows.FILE_ATTRIBUTE_HIDDEN)
	} else {
		return windows.SetFileAttributes(pathp, windows.FILE_ATTRIBUTE_NORMAL)
	}

}

// OpenFile 打开文件 返回文件以及错误信息
func OpenFile(path string) (*File, error) {

	f, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm)
	if err != nil {
		return nil, err
	}
	defer func(f *os.File) {
		_ = f.Close()
	}(f)
	fileinfo, err := f.Stat()
	if err != nil {
		return nil, err
	}

	var createTime int64

	var modifyTime int64

	ishidden := false

	if runtime.GOOS == "windows" {

		//获取文件信息
		winFileAttr := fileinfo.Sys().(*syscall.Win32FileAttributeData)
		createTime = winFileAttr.CreationTime.Nanoseconds() / 1e9
		modifyTime = winFileAttr.LastWriteTime.Nanoseconds() / 1e9
		t, _ := isFileHidden(path)
		if t {
			ishidden = true
		}

	} else if runtime.GOOS == "linux" {

	}

	//ti := time.Now()
	//fmt.Println(ti)
	loc, _ := time.LoadLocation("Asia/Shanghai")
	tf := File{Name: filepath.Base(f.Name()),
		Size:       fileinfo.Size(),
		CreateTime: time.Unix(createTime, 0).In(loc),
		ModifyTime: time.Unix(modifyTime, 0).In(loc),
		Path:       path, Ext: filepath.Ext(path),
		IsHidden: ishidden,
	}

	mode := fileinfo.Mode()
	if mode&0400 == 0400 {
		tf.IsCanRead = true
	} else {
		tf.IsCanRead = false
	}
	if mode&0200 == 0200 {
		tf.IsCanWrite = true
	} else {
		tf.IsCanWrite = false
	}

	return &tf, nil

}
